// 文件路径: dbms/execution/execution/ExplainExecutor.java
package com.dbms.execution.execution;

import com.dbms.common.Column;
import com.dbms.common.Schema;
import com.dbms.common.Tuple;
import com.dbms.common.Type;
import com.dbms.execution.plan.PlanNode;

import java.util.List;

/**
 * ExplainExecutor (解释执行器) 负责执行 EXPLAIN 和 EXPLAIN ANALYZE 命令。
 * 它的工作方式是：
 * 1. 接收一个待分析的查询的执行器计划 (childPlan)。
 * 2. 为了获取 `ANALYZE` 所需的性能数据（如执行时间、产出的行数），它会完整地执行一遍子计划。
 * 3. 执行完毕后，它从子计划中提取带有性能数据的计划树 (PlanNode)。
 * 4. 最后，它将这个计划树格式化为一个字符串，并作为单行单列的结果返回。
 */
public class ExplainExecutor implements Executor {

    /**
     * 存储从已执行的子计划中获取的、带有性能数据的计划树。
     */
    private final PlanNode plan;

    /**
     * EXPLAIN 命令输出的模式，固定为单列 `(QUERY PLAN: VARCHAR)`。
     */
    private final Schema outputSchema;

    /**
     * 标志位，确保只输出一次结果。
     */
    private boolean done = false;

    /**
     * 构造函数
     * @param childPlan 要解释的查询的执行计划树的根执行器。
     */
    public ExplainExecutor(Executor childPlan) {
        // 1. 【关键步骤】为了收集性能数据 (ANALYZE)，必须完整地执行子计划。
        childPlan.open();
        // 通过不断调用 next() 来拉取所有结果，确保整个计划被从头到尾执行完毕。
        while (childPlan.next() != null) {
            // 我们不关心子计划的输出内容，只是为了驱动它执行。
        }
        childPlan.close();

        // 2. 从执行完毕的子计划中获取带有性能数据的 PlanNode。
        // 此时，PlanNode 内部的执行时间、行数等指标已经被填充。
        this.plan = childPlan.getPlanNode();

        // 3. 定义 EXPLAIN 的输出 Schema，只有一个名为 "QUERY PLAN" 的字符串列。
        this.outputSchema = new Schema(List.of(
                new Column("QUERY PLAN", Type.VARCHAR, 1024)
        ));
    }

    /**
     * 打开执行器，准备输出。
     */
    @Override
    public void open() {
        this.done = false;
    }

    /**
     * 生成并返回包含查询计划的元组。
     * @return 第一次调用时返回一个包含格式化计划字符串的元组，之后调用返回 null。
     */
    @Override
    public Tuple next() {
        if (!done) {
            done = true;
            // 调用 PlanNode 的 toFormattedString() 方法，生成一个带缩进的、易于阅读的计划树字符串。
            String formattedPlan = plan.toFormattedString();
            // 将这个字符串包装成一个元组返回。
            return new Tuple(List.of(formattedPlan), outputSchema);
        }
        return null;
    }

    /**
     * 关闭执行器。
     */
    @Override
    public void close() {
        // 没有需要释放的资源
    }

    /**
     * 返回输出的 Schema。
     */
    @Override
    public Schema getSchema() {
        return outputSchema;
    }

    /**
     * 获取计划节点。
     * ExplainExecutor 本身不参与查询计划树，它只是一个包装器和展示工具，
     * 因此它没有自己的 PlanNode。
     * @return 总是返回 null。
     */
    @Override
    public PlanNode getPlanNode() {
        return null;
    }
}